﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Optimizer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private double F(Point p)
        {
            return Power2(p.X1) + Power2(p.X2) - Math.Cos(18 * p.X1) - Math.Cos(18 * p.X2);            
        }

        private void AddMenuItem<T>()
            where T: IOptimizer, new()
        {
            var t = new T();
            var menuItem = new MenuItem() { Header = t.Name() };
            menuItem.Click +=
                (a, b) =>
                {
                    this.Path.Clear();
                    this.DrawArray(0, 0, this.Array);
                    var i = 0;
                    var Min = double.MaxValue;
                    var OptX = new T().Do(
                        (x) =>
                        {
                            var y = F(x);
                            uint Suc = 0xFF000000;
                            if (y<Min) 
                            {
                                Min=y;
                                Suc = 0xFFFFFFFF;
                            }
                            this.AddPath(x, Suc);
                            ++i;
                            //if (Suc == 0xFFFFFFFF) this.Label.Content = "Кількість пробних точок: " + i.ToString() + "; Вихідна точка F(" + x.X1 + ";" + x.X2 + ")=" + Min;
                            return y;
                        });
                    this.AddPath(OptX, 0xFFFFFFFF);
                    this.Label.Content = "Кількість пробних точок: " + i.ToString() + "; Вихідна точка F ( " + OptX.X1.ToString("0.#####") + " ; " + OptX.X2.ToString("0.#####") + " ) = " + Min.ToString("0.#####");
                };
            this.AlgorithmList.Items.Add(menuItem);
        }

        public MainWindow()
        {
            InitializeComponent();
            //
            this.AddMenuItem<Mixed>();
            this.AddMenuItem<MixedNew>();
            this.AddMenuItem<Stupid>();
            this.AddMenuItem<Study>();
        }

        // private const int Size = 10;

        private List<Point> Path = new List<Point>();

        private WriteableBitmap BitMap = null;

        private uint[,] Array = null;

        private void DrawArray(int x, int y, uint[,] array)
        {
            var yCount = array.GetLength(0);
            var xCount = array.GetLength(1);
            this.BitMap.WritePixels(
                new Int32Rect(0, 0, xCount, yCount),
                array,
                xCount * 4,
                x,
                y);
        }

        private void Draw(Func<Point, double> f, double k)
        {
            var xCount = (int)this.WrapPanel.ActualWidth;
            var yCount = (int)this.WrapPanel.ActualHeight;
            this.BitMap =
                new WriteableBitmap(
                    xCount, yCount, 96, 96, PixelFormats.Bgra32, null);
            this.Array = new uint[yCount, xCount];
            var zArray = new double[yCount, xCount];
            var zMax = double.MinValue;
            var zMin = double.MaxValue;
            for (int y = 0; y < yCount; ++y)
            {
                // var yD = (y + (double)Size / 2) / yCount;
                var yD = (double)y / yCount;
                for (int x = 0; x < xCount; ++x)
                {
                    // var xD = (x + (double)Size / 2) / xCount;
                    var xD = (double)x / xCount;
                    var z = f(new Point() { X1 = xD, X2 = yD });
                    zMax = Math.Max(zMax, z);
                    zMin = Math.Min(zMin, z);
                    zArray[y, x] = z;
                }
            }
            var deltaZ = zMax - zMin;
            for (int x = 0; x < xCount; ++x)
            {
                for (int y = 0; y < yCount; ++y)
                {
                    var z = (zArray[y, x] - zMin) / deltaZ;
                    z = Math.Pow(z, k);
                    z *= 255;
                    var zUI = (uint)(z + 0.5);
                    this.Array[y, x] =
                        (zUI) +
                        ((255 - zUI) << 8) +
                        ((255 - zUI) << 16) +
                        0xFF000000;
                }
            }
            this.DrawArray(0, 0, this.Array);
            this.Image.Source = this.BitMap;
        }

        private static double Power2(double x)
        {
            return x * x;
        }
        
        /// <summary>
        /// X, X1: [0;1] => [-1;1]
        /// Y, X2: [0;1] => [-1;1]
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private static Point Cast(Point x)
        {
            return new Point()
            {
                X1 = x.X1 * 2 - 1,
                X2 = x.X2 * 2 - 1,
            };
        }

        private static Point InvertCast(Point x)
        {
            return new Point()
            {
                X1 = (x.X1 + 1) / 2,
                X2 = (x.X2 + 1) / 2,
            };
        }

        private void Draw()
        {
            this.Draw(
                (p) =>
                {
                    return F(Cast(p));
                },
                1.0 / 8.0);
        }

        private void AddPath(Point p, uint Suc)
        {
            p = InvertCast(p);
            this.Path.Add(p);
            //
            var yCount = this.Array.GetLength(0);
            var xCount = this.Array.GetLength(1);
            var y = (int)(p.X2 * yCount + 0.5);
            var x = (int)(p.X1 * xCount + 0.5);
            y =
                y < 0 ? 0 :
                y >= yCount ? yCount - 1 :
                y;
            x =
                x < 0 ? 0 :
                x >= xCount ? xCount - 1 :
                x;
            var array = new uint[1, 1] { { Suc } };
            this.DrawArray(x, y, array);
        }

        /*private void Run_Click(object sender, RoutedEventArgs e)
        {
            this.Path.Clear();
            this.DrawArray(0, 0, this.Array);
            var i = 0;
            new Mixed().Do(
                (x) =>
                {
                    this.AddPath(x);
                    ++i;
                    this.Label.Content = "# " + i.ToString();
                    return F(x);
                });
        }*/

        private void WrapPanel_SizeChanged(
            object sender, SizeChangedEventArgs e)
        {
            this.Draw();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.Draw();
        }

        private void Exit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
    }
}
